//!
//! simple PCI-Express initialization, only works for qemu and its e1000 card.
//!

comptime {
    driver.register(&init, "pci-host-ecam-generic");
}

var e1000_dev: usize = 0;

fn init(arg: ?*const anyopaque) void {
    // qemu -machine virt puts PCIe config space here.
    const node: *const os.fdt.Node = @ptrCast(@alignCast(arg.?));
    const base = os.fdt.base(node);
    const ecam: [*]volatile u32 = @ptrFromInt(base);

    // look at each possible PCI device on bus 0.
    for (0..32) |dev| {
        const bus = 0;
        const func = 0;
        const offset = 0;

        // https://github.com/qemu/qemu/blob/v10.0.0/include/hw/pci/pcie_host.h#L61-L66
        const addr = bus << 20 | dev << 15 | func << 12 | offset;
        const reg = ecam[addr / @sizeOf(u32) ..];
        const header: *align(1) volatile Ccsh = @ptrCast(@alignCast(reg));

        // 8086:100e is an e1000
        if (header.verdor == 0x8086 and header.device == 0x100e) {
            header.command = .{ .ise = true, .mse = true, .bme = true };
            fence();

            // [E1000 4.1] PCI Configuration
            for (4..10) |i| {
                const old = reg[i];

                // writing all 1's to the Base Address Register causes it to be replaced with its size.
                reg[i] = 0xffff_ffff;
                fence();

                reg[i] = old;
            }

            // tell the e1000 to reveal its registers at physical address 0x40000000.
            reg[4] = os.arch.qemu.E1000;

            e1000.init(os.arch.qemu.E1000) catch @panic("e1000 init failed");
            e1000_dev = dev;
            break;
        }
    }
}

pub fn intr(dev: usize) void {
    if (dev == e1000_dev) e1000.intr();
}

// [PCIE 7.5.1.1]
/// Type 0/1 Common Configuration Space
const Ccsh = packed struct(u128) {
    verdor: u16,
    device: u16,
    command: Command,
    status: u16,

    revision: u8,
    class: u24,

    size: u8,
    latency: u8,
    type: u8,
    bist: u8,

    const Command = packed struct(u16) {
        /// I/O Space Enable
        ise: bool = false,
        /// Memory Space Enable
        mse: bool = false,
        /// Bus Master Enable
        bme: bool = false,
        /// Special Cycle Enable
        sce: bool = false,
        /// Memory Write and Invalidate
        mwi: bool = false,
        /// VGA Palette Snoop
        vps: bool = false,
        /// Parity Error Response
        per: bool = false,
        /// IDSEL Stepping/Wait Cycle Control
        iswcc: bool = false,
        /// SERR# Enable
        se: bool = false,
        /// Fast Back-to-Back Transactions Enable
        fbte: bool = false,
        /// Interrupt Disable
        id: bool = false,
        _: u5 = 0,
    };
};

const std = @import("std");
const os = @import("../os.zig");
const fence = os.Lock.fence;
const e1000 = @import("e1000.zig");
const driver = @import("../driver.zig");
